<script setup lang="ts" name="VerifyPoints">
/**
 * VerifyPoints
 * @description 点选
 * */
import { storeToRefs } from 'pinia'
import { generateRandomPosition, getRandomUniqueIndexes, textPositionType, isOverlapPosition, isOverlap } from './helper'
import { useAppStoreWithOut } from '@/store/modules/app'
import { PREDEFINE_COLORS } from '@/utils/color'
import image1 from '@/assets/captcha/clickWord/1.jpg'
import image2 from '@/assets/captcha/clickWord/2.jpg'
import image3 from '@/assets/captcha/clickWord/3.jpg'
import image4 from '@/assets/captcha/clickWord/4.jpg'
import image5 from '@/assets/captcha/clickWord/5.jpg'
import image6 from '@/assets/captcha/clickWord/6.jpg'
import image7 from '@/assets/captcha/clickWord/7.jpg'
import image8 from '@/assets/captcha/clickWord/8.jpg'
import image9 from '@/assets/captcha/clickWord/9.jpg'

const appStore = useAppStoreWithOut()
const { systemConfig } = storeToRefs(appStore)
const { t } = useI18n()
const props = defineProps({
  captchaType: {
    type: String,
    default: ''
  },
  mode: {
    type: String,
    default: 'dialog' // dialog (弹出式) | hover（触发式） | embedded（嵌入式）
  },
  size: {
    type: String,
    default: '' // '' (系统默认) | default（默认大小）| large（大） | small（小）
  }
})

const emit = defineEmits(['success', 'error'])
const { mode, size } = toRefs(props)
const VerifyImageWrapRef: any = ref()
const VerifyImagePanelRef: any = ref()
const VerifySlideControlRef: any = ref()
const sortwidth = '26px'
const sortheight = '33px'
const initInfo: any = ref({
  visible: false,
  loading: false,
  size: {
    text: 'default',
    value: '32px'
  },
  pool: {
    font: '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章爱育黎首臣伏戎羌遐迩体率宾归王',
    picture: [image1, image2, image3, image4, image5, image6, image7, image8, image9],
    color: PREDEFINE_COLORS
  },
  rule: {
    clickCount: 3,
    count: 5,
    width: 30,
    angleRange: 60
  },
  data: {
    image: '',
    position: [] as textPositionType[],
    clickPosition: [] as textPositionType[],
    text: [],
    clickText: []
  },
  value: {
    isPass: undefined,
    click: []
  }
})

// 获取点击文字验证码的图片及文字
const initClickWordData = () => {
  // 获取背景图
  initInfo.value.data.image = initInfo.value.pool.picture[Math.floor(Math.random() * initInfo.value.pool.picture.length)]
  nextTick(() => {
    // 获取需要展示的文字
    const newPositions: textPositionType[] = [] // 用来生成文字定位信息
    // （所需字符串的长度）    initInfo.value.rule.count：生成点击文字的个数
    let textOptionArr = getRandomUniqueIndexes(initInfo.value.pool.font.length, initInfo.value.rule.count)
    let textSelectString: any = [] // 文本顺序
    let textColorArr: any = getRandomUniqueIndexes(initInfo.value.pool.color.length, initInfo.value.rule.count)
    for (let i = 0; i < initInfo.value.rule.count; i++) {
      // 位置信息     图片宽度    图片高度    文本宽度
      const newPosition = generateRandomPosition(newPositions, VerifyImagePanelRef.value.offsetWidth, VerifyImagePanelRef.value.offsetHeight, initInfo.value.rule.width, initInfo.value.rule.width, initInfo.value.rule.angleRange);
      newPositions.push({ ...newPosition, text: initInfo.value.pool.font[textOptionArr[i]], color: initInfo.value.pool.color[textColorArr[i]] })
      textSelectString.push(initInfo.value.pool.font[textOptionArr[i]])
    }
    initInfo.value.data.text = textSelectString
    console.log("newPositions", newPositions)
    initInfo.value.data.position = newPositions
    // 获取需要点击的文字
    let clickTextOptionArr = getRandomUniqueIndexes(initInfo.value.data.text.length, initInfo.value.rule.clickCount)
    let clickTextSelectString: any = [] // 文本顺序
    for (let i = 0; i < initInfo.value.rule.clickCount; i++) {
      clickTextSelectString.push(initInfo.value.data.text[clickTextOptionArr[i]])
      const index = initInfo.value.data.position.findIndex(item => item.text === initInfo.value.data.text[clickTextOptionArr[i]])
      initInfo.value.data.clickPosition.push(initInfo.value.data.position[index])
    }
    initInfo.value.data.clickText = clickTextSelectString
    console.log(initInfo.value.data.clickPosition)
    initInfo.value.loading = false
    initInfo.value.value.isPass = undefined
  })
}

// 验证码点击事件
const maskClick = (e) => {
  // 已验证过，再次点击则刷新验证图片及文字
  if (initInfo.value.value.click.length === initInfo.value.rule.clickCount) {
    init()
  } else {
    const rect = VerifyImagePanelRef.value.getBoundingClientRect()
    const x = e.clientX - rect.left
    const y = e.clientY - rect.top
    const itemPoint = { x, y, width: parseInt(sortwidth), height: parseInt(sortheight), left: x - Math.round(parseInt(sortwidth) / 2) + 2, top: y - parseInt(sortheight) + 4 }
    let isCancel = false
    if (initInfo.value.value.click.length > 0) {
      for (var i = 0; i < initInfo.value.value.click.length; i++) {
        if (isOverlapPosition(itemPoint, initInfo.value.value.click[i]) && i === initInfo.value.value.click.length - 1) {
          isCancel = true
          initInfo.value.value.click.splice(i, 1)
        }
      }
    }
    if (!isCancel) {
      initInfo.value.value.click.push(itemPoint)
      console.log(initInfo.value.value.click)
      // 新增一个点击点后，判断是否已经点击足够个文字
      if (initInfo.value.value.click.length === initInfo.value.rule.clickCount) {
        // 点击个数已达标准，需判断点击顺序是否与结果一致
        if (isChecked()) {
          initInfo.value.value.isPass = true
          var captchaVerification = true
          emit('success', { captchaVerification })
        } else {
          initInfo.value.value.isPass = false
          emit('error')
          setTimeout(function () {
            refresh()
          }, 1000)
        }
      }
    }
  }
}

const isChecked = () => {
  return initInfo.value.value.click.every((item, index) => {
    return isOverlap(item, initInfo.value.data.clickPosition[index])
  })
}

// 初始化
const init = () => {
  initInfo.value.loading = true
  initInfo.value.value.click = []
  initInfo.value.value.isPass = undefined
  initInfo.value.data.image = ''
  initInfo.value.data.position = []
  initInfo.value.data.text = []
  initInfo.value.data.clickText = []
  initInfo.value.data.clickPosition = []
  setTimeout(() => {
    initClickWordData()
  }, 1000)
}

// 刷新
const refresh = async () => {
  init()
}

defineExpose({ refresh })

// 初始化组件尺寸
const initSize = () => {
  if (size.value) {
    initInfo.value.size.text = size.value
  } else {
    initInfo.value.size.text = systemConfig.value.size
  }
  const sizeText = initInfo.value.size.text === 'default' ? '' : '-' + initInfo.value.size.text
  const htmlElement = document.querySelector('html') as HTMLHtmlElement | null
  if (htmlElement) {
    const root = getComputedStyle(htmlElement)
    initInfo.value.size.value = root.getPropertyValue('--el-component-size' + sizeText)
  }
  console.log(initInfo.value.size.text, initInfo.value.size.value)
}

// 监听尺寸变化
watch(
  () => systemConfig.value.size,
  () => {
    initSize()
  },
  {
    immediate: true
  }
)


const handleMouseEnter = () => {
  if (mode.value === 'hover') {
    initInfo.value.visible = true
  }
}

const handleMouseLeave = () => {
  if (mode.value === 'hover') {
    window.addEventListener('touchmove', handleMouseMove)
    window.addEventListener('mousemove', handleMouseMove)
  }
}

const handleMouseMove = (e) => {
  e = e || window.event
  if (!e.touches) {
    //兼容PC端
    var x = e.clientX
    var y = e.clientY
  } else {
    //兼容移动端
    var x = e.touches[0].pageX
    var y = e.touches[0].pageY
  }
  const otherDomRect = VerifyImageWrapRef.value.getBoundingClientRect()
  const enterDomRect = VerifySlideControlRef.value.getBoundingClientRect()
  if (x >= enterDomRect.left && x <= enterDomRect.right && y >= enterDomRect.top && y <= enterDomRect.bottom ) {
    initInfo.value.visible = true
  } else {
    if (!(x >= otherDomRect.left && x <= otherDomRect.right && y >= otherDomRect.top && y <= otherDomRect.bottom)) {
      initInfo.value.visible = false
      window.removeEventListener('touchend', handleMouseMove)
      window.removeEventListener('mouseup', handleMouseMove)
    }
  }
}

// 初始化组件
onMounted(() => {
  init()
})
</script>

<template>
  <div class="verify-box" :class="`verify-box-${initInfo.size.text}`">
    <div ref="VerifyImageWrapRef" class="verify-image-wrap" :class="[mode === 'hover' ? 'is-hover-mode' : '', initInfo.visible ? 'is-visible' : '']">
      <div ref="VerifyImagePanelRef" class="verify-image-panel">
        <img class="verify-bg-image" :src="initInfo.data.image" />
        <div
          v-for="(item, indexS) in initInfo.data.position"
          :key="indexS"
          :style="{
            left: item.x + 'px',
            top: item.y + 'px',
            width: initInfo.rule.width + 'px',
            height: initInfo.rule.width + 'px',
            transform: `rotate(${item.transform}deg)`,
            fontSize: 24 + 'px',
            color: item.color,
            lineHeight: `${initInfo.rule.width}px`}"
          class="item-text">
          <span>{{ item.text }}</span>
        </div>
        <div class="mask" @click="maskClick">
          <div v-for="(item, index) in initInfo.value.click" :key="index" :class="['item-point', 'item-point-' + (index + 1)]" :style="{left: item.left + 'px', top: item.top + 'px' }" />
        </div>
        <div v-if="!initInfo.value.isPass" class="verify-refresh" @click="refresh">
          <SvgIcon icon="ep:refresh-right" size="20px" />
        </div>
        <div class="loading" :class="initInfo.loading ? 'is-loading-now' : ''">
          <SvgIcon icon="ep:loading" class="is-loading" />
          <span>{{ t('common', 'loadingText') }}</span>
        </div>
      </div>
    </div>
    <!-- 公共部分 -->
    <div ref="VerifySlideControlRef" class="verify-slide-control" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
      <template v-if="mode === 'hover'">
        <div v-if="!initInfo.visible && !initInfo.value.isPass" class="verify-tips">
          {{ t('captcha', 'tip') }}
        </div>
        <template v-else>
          <div v-if="initInfo.loading" class="verify-tips">
            {{ t('common', 'loadingText') }}
          </div>
          <template v-else>
            <div v-if="initInfo.value.isPass === undefined" class="verify-tips">
              {{ t('captcha', 'point') }}
              <span v-for="(item, index) in initInfo.data.clickText" :key="index">"{{ item }}"</span>
            </div>
            <div v-if="initInfo.value.isPass === true" class="verify-tips is-success">
              <SvgIcon icon="ep:check" />
              {{ t('captcha', 'success') }}
            </div>
            <div v-if="initInfo.value.isPass === false" class="verify-tips is-error">
              <SvgIcon icon="ep:close" />
              {{ t('captcha', 'fail') }}
            </div>
          </template>
        </template>
      </template>
      <template v-else>
        <div v-if="initInfo.loading" class="verify-tips">
          {{ t('common', 'loadingText') }}
        </div>
        <template v-else>
          <div v-if="initInfo.value.isPass === undefined" class="verify-tips">
            {{ t('captcha', 'point') }}
            <span v-for="(item, index) in initInfo.data.clickText" :key="index">"{{ item }}"</span>
          </div>
          <div v-if="initInfo.value.isPass === true" class="verify-tips is-success">
            <SvgIcon icon="ep:check" />
            {{ t('captcha', 'success') }}
          </div>
          <div v-if="initInfo.value.isPass === false" class="verify-tips is-error">
            <SvgIcon icon="ep:close" />
            {{ t('captcha', 'fail') }}
          </div>
        </template>
      </template>
    </div>
  </div>
</template>

<style scoped lang="scss">
.verify-box {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;

  &-default {
    --el-verify-size: var(--el-component-size);

    .verify-slide-button {
      .el-icon {
        font-size: 20px !important;
      }
    }
  }

  &-large {
    --el-verify-size: var(--el-component-size-large);

    .verify-slide-button {
      .el-icon {
        font-size: 24px !important;
      }
    }
  }

  &-small {
    --el-verify-size: var(--el-component-size-small);

    .verify-slide-button {
      .el-icon {
        font-size: 16px !important;
      }
    }
  }
}

.verify-image-wrap {
  position: relative;
  z-index: 1;
  width: 100%;
  padding-bottom: 16px;
  overflow: hidden;
  pointer-events: auto;
  border-radius: 2px;
  user-select: none;

  &.is-hover-mode {
    position: absolute;
    bottom: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity .3s linear, bottom .3s linear;

    &.is-visible {
      bottom: var(--el-verify-size);
      opacity: 1;
      visibility: visible;
    }
  }
}

.verify-image-panel {
  position: relative;
  z-index: 1;
  width: 100%;
  padding-top: 50%;
  overflow: hidden;
  pointer-events: auto;
  border-radius: 2px;
  user-select: none;

  .verify-bg-image {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background-repeat: no-repeat;
    background-size: 100%;
    border-radius: 2px;
    user-select: none;
    perspective: 1000;
    -webkit-user-drag: none;
  }

  .item-text{
    position: absolute;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    transform-origin: 50% 50%;
  }

  .mask {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    width: 100%;
    height: 100%;

    .item-point{
      position: absolute;
      width: v-bind(sortwidth);
      height: v-bind(sortheight);
      background-image: url('@/assets/captcha/sort.png');
      background-repeat: no-repeat;

      &-1{
        background-position: 0 0;
      }

      &-2{
        background-position: 0 -38px;
      }

      &-3{
        background-position: 0 -76px;
      }

      &-4{
        background-position: 0 -114px;
      }

      &-5{
        background-position: 0 -152px;
      }
    }
  }

  .verify-refresh {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 4;
    display: flex;
    width: 28px;
    height: 28px;
    color: var(--el-color-white);
    text-align: center;
    pointer-events: auto;
    cursor: pointer;
    background: rgba(0, 0, 0, 0.2);
    align-items: center;
    justify-content: center;
  }

  .loading {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 10;
    display: flex;
    width: 100%;
    height: 100%;
    background: var(--el-fill-color);
    opacity: 0;
    visibility: hidden;
    transition: opacity .1s linear;
    align-items: center;
    justify-content: center;
    flex-direction: column;

    &.is-loading-now {
      opacity: 1;
      visibility: visible;
    }

    span {
      margin-top: 10px;
      font-size: 14px;
    }
  }
}

.verify-slide-control {
  position: relative;
  z-index: 2;
  height: var(--el-verify-size);
  background-color: var(--el-fill-color-light);
  border: 1px solid var(--el-border-color);
  border-radius: 2px;

  .verify-tips {
    position: absolute;
    bottom: 0;
    left: 0;
    z-index: 1;
    display: flex;
    width: 100%;
    height: 100%;
    color: var(--el-text-color-primary);
    align-items: center;
    justify-content: center;
    user-select: none;

    &.is-success {
      color: var(--el-color-success);
      background-color: var(--el-color-success-light-9);
      border: 1px solid var(--el-color-success);
    }

    &.is-error {
      color: var(--el-color-error);
      background-color: var(--el-color-error-light-9);
      border: 1px solid var(--el-color-error);
    }

    .el-icon {
      margin-right: 6px;
      font-size: 20px!important;
    }

    span {
      font-weight: bold;
    }
  }
}
</style>
